/*
 *    This file is part of SSHTunneler.
 *
 *    Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
 *
 *    SSHTunneler is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    SSHTunneler is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with SSHTunneler.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package sshtunneler.tools;

import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipException;

import javax.swing.JMenu;
import javax.swing.JMenuItem;

import sshtunneler.library.Server;
import sshtunneler.library.ServerContainer;
import sshtunneler.library.Tunnel;

public class ToolsManager {

	public static ToolsManager inst = null;
	public static final String tools_package = "sshtunneler.tools";

	public static void init(ServerContainer<?> container, String directory) {
		inst = new ToolsManager(container);

		try {
			inst.load_subclasses_in(directory);
		} catch (ZipException ex) {
			Logger.getLogger(ToolsManager.class.getName()).log(Level.SEVERE,
					null, ex);
		} catch (IOException ex) {
			Logger.getLogger(ToolsManager.class.getName()).log(Level.SEVERE,
					null, ex);
		}
	}

	protected List<Tool> tools = new ArrayList<Tool>();
	protected HashMap<Object, Tool> menu_map = new HashMap<Object, Tool>();
	private ServerContainer<?> container;

	protected ToolsManager(ServerContainer<?> container) {
		this.container = container;
	}

	public Iterable<Tool> all_tools() {
		return this.tools;
	}

	public ServerContainer<?> getContainer() {
		return this.container;
	}

	public boolean manages(Tool tool) {
		for (Tool t : this.tools) {
			if (t.getClass().equals(tool.getClass())) {
				return true;
			}
		}
		return false;
	}

	protected void load_subclasses_in(String directorypath)
			throws ZipException, IOException {
		ArrayList<File> classnames_to_search = new ArrayList<File>();

		File directory = new File(directorypath + File.separator
				+ tools_package.replace(".", File.separator));
		Logger.getLogger(ToolsManager.class.getName()).log(Level.INFO,
				"Looking for tools in: " + directorypath);

		if (directory.exists() && directory.isDirectory()) {
			for (File f : directory.listFiles()) {
				classnames_to_search.add(f);
			}
		} else {
			return;
		}
		File directory_root = new File(directorypath);
		URI uri = directory_root.toURI();
		URL url = uri.toURL();
		URL[] urls = new URL[] { url };
		ClassLoader cl = new URLClassLoader(urls);

		for (File f : classnames_to_search) {
			String current_file_name = f.getName();
			if (current_file_name.endsWith(".class")) {
				String classname = current_file_name.substring(0,
						current_file_name.length() - 6);
				if (classname.equals("ToolsManager")) {
					continue;
				}
				try {
					Class<?> cls = cl
							.loadClass(tools_package + "." + classname);
					Object o = cls.newInstance();
					if (Tool.class.isInstance(o)) {
						Tool t = (Tool) o;
						t.from_directory = directory;
						File file = this.prop_file(t);
						Logger.getLogger(ToolsManager.class.getName()).log(
								Level.INFO, "Loaded tool: " + classname);
						t.manager = this;
						t.init_properties();
						t.load_properties(file);
						this.tools.add(t);
					}
				} catch (ClassNotFoundException cnfex) {
					Logger.getLogger(ToolsManager.class.getName()).log(
							Level.WARNING,
							"(ClassNotFoundException)Error loading \""
									+ tools_package + "." + classname + "\": "
									+ cnfex.getMessage());
				} catch (InstantiationException iex) {
					Logger.getLogger(ToolsManager.class.getName()).log(
							Level.WARNING,
							"(InstantiationException)Error loading \""
									+ tools_package + "." + classname + "\": "
									+ iex.getMessage());
				} catch (IllegalAccessException iaex) {
					Logger.getLogger(ToolsManager.class.getName()).log(
							Level.WARNING,
							"(IllegalAccessException)Error loading \""
									+ tools_package + "." + classname + "\": "
									+ iaex.getMessage());
				}
			}
		}
	}

	protected File prop_file(Tool t) {
		String classname = t.getClass().getName();
		if (classname.lastIndexOf('.') != -1) {
			classname = classname.substring(classname.lastIndexOf('.') + 1);
		}
		return new File(t.from_directory, classname + ".properties");
	}

	public void save_all_tool_config() {
		for (Tool t : this.tools) {
			File file = this.prop_file(t);
			t.save_properties(file);
		}
	}

	public JMenuItem get_tunnel_tools_menu(Server server, Tunnel tunnel,
			boolean server_connected, boolean tunnel_connected,
			ActionListener listener) {
		JMenu main_menu = new JMenu("Tools");
		this.menu_map.clear();

		for (Tool tool : this.tools) {
			tool.reset();
			JMenuItem current = tool.get_tunnel_tools_menu(server, tunnel,
					server_connected, tunnel_connected);
			if (current != null) {
				this.menu_map.put(current, tool);
				main_menu.add(current);
				current.removeActionListener(listener);
				current.addActionListener(listener);
			}
		}

		if (main_menu.getItemCount() == 0) {
			main_menu.setEnabled(false);
		}

		return main_menu;
	}

	public JMenuItem get_server_tools_menu(Server server,
			boolean server_connected, ActionListener listener) {
		JMenu main_menu = new JMenu("Tools");
		this.menu_map.clear();

		for (Tool tool : this.tools) {
			tool.reset();
			JMenuItem current = tool.get_server_tools_menu(server,
					server_connected);
			if (current != null) {
				this.menu_map.put(current, tool);
				main_menu.add(current);
				current.removeActionListener(listener);
				current.addActionListener(listener);
			}
		}

		if (main_menu.getItemCount() == 0) {
			main_menu.setEnabled(false);
		}

		return main_menu;
	}

	public JMenuItem get_main_tools_menu(ActionListener listener) {
		JMenu main_menu = new JMenu("Tools");
		this.menu_map.clear();

		for (Tool tool : this.tools) {
			tool.reset();
			JMenuItem current = tool.get_main_tools_menu();
			if (current != null) {
				this.menu_map.put(current, tool);
				main_menu.add(current);
				current.removeActionListener(listener);
				current.addActionListener(listener);
			}
		}

		if (main_menu.getItemCount() == 0) {
			main_menu.setEnabled(false);
		}

		return main_menu;
	}

	public MenuItem get_tray_tools_menu(ActionListener listener) {
		Menu main_menu = new Menu("Tools");
		this.menu_map.clear();

		for (Tool tool : this.tools) {
			MenuItem current = tool.get_tray_tools_menu();
			if (current != null) {
				this.menu_map.put(current, tool);
				main_menu.add(current);
				current.removeActionListener(listener);
				current.addActionListener(listener);
			}
		}

		if (main_menu.getItemCount() == 0) {
			main_menu.setEnabled(false);
		}

		return main_menu;
	}

	public void process_event(Object selected) {
		Tool associated_tool = this.menu_map.get(selected);
		if (associated_tool != null) {
			associated_tool.run();
		}
	}
}
